package com.android.nurse;

import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.List;

import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.NameValuePair;
import org.apache.http.StatusLine;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.utils.URLEncodedUtils;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.params.HttpConnectionParams;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import android.content.Context;
import android.util.Log;

public class LocalRanking {
	private LocalRankingInfo newRank;
	private ArrayList<LocalRankingInfo> listLocalRank;
	private Context context;
	private String rankString;
	private ArrayList<LocalRankingInfo> listServerRank;
	public static boolean addedNew = false;
	private static final int REMOVE = 1;
	private static final int ADD = 2;
	private static final int POST = 3;
	private static final int GET = 4;

	/* >>>>>>>>>>> SET GET area <<<<<<<<<<<<<<<<< */
	/**
	 * Make a new object
	 */
	public LocalRanking(LocalRankingInfo newRank, Context context) {
		this.newRank = newRank;
		listLocalRank = new ArrayList<LocalRankingInfo>();
		this.context = context;
		this.makeNewRankList();
	}

	public LocalRankingInfo getNewRank() {
		return newRank;
	}

	public void setNewRank(LocalRankingInfo newRank) {
		this.newRank = newRank;
	}

	public ArrayList<LocalRankingInfo> getListServerRank() {
		return listServerRank;
	}

	public void setListServerRank(ArrayList<LocalRankingInfo> listServerRank) {
		this.listServerRank = listServerRank;
	}

	public static boolean isAddedNew() {
		return addedNew;
	}

	/* >>>>>>>>>>>>>>>>>>>>>>>>>> ########## <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< */
	/**
	 * This function check if show_all flag true it will get global rank,and
	 * return list LocalRankInfo object.
	 */

	public ArrayList<LocalRankingInfo> getListLocalRankToShow(boolean showFlag,
			String url) {
		/* Kiem tra neu file rank khong null */
		if (listLocalRank.size() > 0) {

			/* Kiem tra neu thiet dinh hien thi rank va url khong null */
			if (showFlag && url != null) {

				if (this.updateGlobalRankForLocalFile(this.makeUrl(url, 2),
						showFlag, GET)) {

					Log.d("update", "Update success");
					return listLocalRank;

				} else {

					Log.d("update", "Update faile");
					for (int i = 0; i < listLocalRank.size(); i++) {
						listLocalRank.get(i).setGlobalPosition("null");
					}
					return listLocalRank;

				}
			} else {
				return listLocalRank;
			}
		} else {
			return null;
		}
	}

	public void postResultToServer(String url,boolean showFlag) {
		if (url != "") {
			if (!this.updateGlobalRankForLocalFile(this.makeUrl(url, 1),showFlag, POST)) {
				this.writeToWaitListFile(newRank, ADD);
			}
		}
	}

	/**
	 * This function update local ranking of this program
	 * 
	 * @author hoangnn
	 * @param None
	 * @return void
	 */
	private void makeNewRankList() {

		/* Doc file ranking */
		rankString = readFile("/ranking.json");
		if (rankString != null) {
			try {
				parserJson(rankString);
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		/* Neu doi tuong chua duoc them vao va la 1 doi tuong khac null */
		if (newRank != null && !addedNew) {

			/* Neu trong ranking list co hon 10 phan tu */
			if (rankString != null && listLocalRank.size() >= 10) {
				LocalRankingInfo localRankingInfo = listLocalRank
						.get(listLocalRank.size() - 1);
				if (localRankingInfo.getTiredScore() < newRank.getTiredScore()) {
					listLocalRank.remove(localRankingInfo);
					listLocalRank.add(newRank);
					addedNew = true;
					updateLocalRankingFile();
				} else {
					if (localRankingInfo.getTiredScore() == newRank
							.getTiredScore()
							&& localRankingInfo.getNurseType() <= newRank
									.getNurseType()) {
						listLocalRank.remove(localRankingInfo);
						listLocalRank.add(newRank);
						addedNew = true;
						updateLocalRankingFile();
					}
				}
			} else {
				/*
				 * Neu trong list co nho hon 10 phan tu va phan tu hien tai chua
				 * dc them vao list
				 */
				if (!addedNew) {
					if (rankString == null) {
						newRank.setPosition(1);
					} else {
						if (!addedNew)
							newRank.setPosition(listLocalRank.size() + 1);
					}
					listLocalRank.add(newRank);
					/* Danh dau da them */
					addedNew = true;
					updateLocalRankingFile();
				}
			}
		} else {
			Log.d("null", "LocalRankNull");
		}

	}

	public void updateAttributeFile(String name, int age, int type) {
		String writeString = "{ \"attribute\":[ {\"nickname\" :\""
				+ newRank.getUsername() + "\",\"age\":" + newRank.getAge()
				+ ",\"type\":" + newRank.getNurseType();
		this.writeFile(writeString, "attribute.json");
	}

	/**
	 * This function make url that request to server
	 * 
	 * @param domain
	 * @param method
	 *            1 is postranking and 2 is getranking
	 * @return
	 */

	private String makeUrl(String domain, int method) {
		String url = domain;
		List<NameValuePair> params = new LinkedList<NameValuePair>();
		if (method == 1) {
			params.add(new BasicNameValuePair("nickname", newRank.getUsername()));
			params.add(new BasicNameValuePair("age", "" + newRank.getAge()));
			params.add(new BasicNameValuePair("tired", ""
					+ newRank.getTiredScore()));
			params.add(new BasicNameValuePair("type", ""
					+ newRank.getNurseType()));
			for (int i = 0; i < listLocalRank.size(); i++) {
				params.add(new BasicNameValuePair("type" + i, ""
						+ newRank.getNurseType()));
				params.add(new BasicNameValuePair("tired" + i, ""
						+ newRank.getTiredScore()));
			}
			String paramString = URLEncodedUtils.format(params, "utf-8");

			url += "?" + paramString;
			Log.d("URL", url);
			return url;
		} else {
			for (int i = 0; i < listLocalRank.size(); i++) {
				params.add(new BasicNameValuePair("type" + i, ""
						+ listLocalRank.get(i).getNurseType()));
				params.add(new BasicNameValuePair("tired" + i, ""
						+ listLocalRank.get(i).getTiredScore()));
			}

			String paramString = URLEncodedUtils.format(params, "utf-8");

			url += "?" + paramString;
			Log.d("URL", url);
			return url;
		}

	}

	/*
	 * Update local ranking file
	 */
	private void updateLocalRankingFile() {
		Collections.sort(listLocalRank, new RankComparator());
		LocalRankingInfo l = new LocalRankingInfo();
		for (int i = 0; i < listLocalRank.size(); i++) {
			//LocalRankingInfo
			l = listLocalRank.get(i);
			l.setPosition(i + 1);
		}

		JSONArray jArray = new JSONArray();
		for (int i = 0; i < listLocalRank.size(); i++) {
			if (i < 10) {
				//LocalRankingInfo l = new LocalRankingInfo();
				l = listLocalRank.get(i);
				JSONObject j = new JSONObject();
				try {
					j.put("no", l.getPosition());
					j.put("type", l.getNurseType());
					j.put("tired", l.getTiredScore());
					j.put("name", l.getUsername());
					j.put("age", l.getAge());
					j.put("all_no", l.getGlobalPosition());
					jArray.put(j);
				} catch (JSONException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
		String stringToWrite = jArray.toString();
		stringToWrite = "{\"ranking\": " + stringToWrite + " }";
		Log.d("WriteString", stringToWrite);
		writeFile(stringToWrite, "ranking.json");
	}

	/**
	 * Read file json from internal storage
	 * 
	 * @return a string save content of file
	 * @author hoangnn
	 */
	private String readFile(String filename) {
		String filePath = context.getFilesDir().getAbsolutePath();
		File file = new File(filePath + filename);
		String line;
		StringBuilder text = new StringBuilder();
		Log.d("file_read", filePath + filename);
		try {
			BufferedReader br = new BufferedReader(new FileReader(file));
			while ((line = br.readLine()) != null) {
				text.append(line);
				text.append('\n');
			}
			Log.d("fileContent", text.toString());
			return text.toString();
		} catch (IOException e) {
			Log.d("fileContent", "null");
			return null;
		}

	}

	/**
	 * Write content of string to internal storage file
	 * 
	 * @param writeString
	 *            string want to write
	 * @return void
	 * @author hoangnn
	 */
	private void writeFile(String writeString, String fileName) {
		try {
			String filePath = context.getFilesDir().getAbsolutePath();
			File f = new File(filePath + "/" + fileName);
			FileWriter fw = new FileWriter(f);
			Log.d("file_write", filePath + "/" + fileName + " string:"
					+ writeString);
			fw.write(writeString);
			fw.close();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();

		}
	}

	/**
	 * Analysis file json
	 * 
	 * @param json
	 *            : content of Json file
	 * @throws Exception
	 */
	private void parserJson(String json) throws Exception {
		if (json != null) {
			Log.d("rankStrings", json);
			JSONObject jObject = new JSONObject(json);
			JSONArray noArray = jObject.getJSONArray("ranking");
			listLocalRank.clear();
			for (int i = 0; i < noArray.length(); i++) {
				LocalRankingInfo l = new LocalRankingInfo();
				JSONObject noRank = noArray.getJSONObject(i);
				l.setPosition(noRank.getInt("no"));
				l.setNurseType(noRank.getInt("type"));
				l.setTiredScore(noRank.getInt("tired"));
				l.setAge(noRank.getInt("age"));
				l.setGlobalPosition(noRank.getString("all_no"));
				l.setUsername(noRank.getString("name"));
				listLocalRank.add(l);
			}
		} else {

		}
	}

	class RankComparator implements Comparator<LocalRankingInfo> {

		@Override
		public int compare(LocalRankingInfo l1, LocalRankingInfo l2) {
			// TODO Auto-generated method stub
			if (l1.getTiredScore() > l2.getTiredScore()) {
				return -1;
			}
			if (l1.getTiredScore() < l2.getTiredScore()) {
				return 1;
			}
			/*
			if (l1.getTiredScore() == l2.getTiredScore()
					&& l1.getNurseType() > l2.getNurseType()) {
				return -1;
			}
			if (l1.getTiredScore() == l2.getTiredScore()
					&& l1.getNurseType() < l2.getNurseType()) {
				return 1;
			}*/
			return 0;
		}

	}

	/**
	 * Execute a request to server.
	 * 
	 * @param url
	 *            of server you want to communicate
	 * @return string response from server if error return null
	 */

	private String makeHttpRequest(String url) {
		HttpClient hClient = new DefaultHttpClient();
		HttpConnectionParams.setConnectionTimeout(hClient.getParams(), 1000); // Timeout
		try {
			Log.d("request", "Vao make request:" + url);
			HttpGet hget = new HttpGet();
			hget.setURI(new URI(url));
			HttpResponse response = hClient.execute(hget);
			StatusLine statusLine = response.getStatusLine();
			if (statusLine.getStatusCode() == HttpStatus.SC_OK) {
				ByteArrayOutputStream out = new ByteArrayOutputStream();
				response.getEntity().writeTo(out);
				out.close();
				String responseString = out.toString();
				Log.d("response", responseString);
				return responseString;
			} else {
				Log.d("response", "Null :" + statusLine.getStatusCode() + " "
						+ statusLine.getReasonPhrase());
				return null;
			}
		} catch (ClientProtocolException e) {
			e.printStackTrace();
			Log.d("response", "Null :");
			return null;
		} catch (IOException e) {
			e.printStackTrace();
			Log.d("response", "Null :");
			return null;
		} catch (URISyntaxException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			Log.d("response", "Null :");
			return null;
		}

	}

	/**
	 * Get list from 1 to 10 position of local rank and server rank
	 * 
	 * @param url
	 *            of server can be use for post of get rank
	 * @return list LocalRankingInfo Object if ok.Else return null
	 */
	private ArrayList<LocalRankingInfo> getServerRank(String url) {
		ArrayList<LocalRankingInfo> listLocalServerRank = new ArrayList<LocalRankingInfo>();
		String result = makeHttpRequest(url);

		/* Check if server error */
		if (result == null) {
			return null;
		} else {
			Log.d("result", result);
			try {

				JSONObject json = new JSONObject(result);
				JSONArray jResultArray = json.getJSONArray("result");
				JSONObject jResultObject = jResultArray.getJSONObject(0);
				Log.d("vao",
						"Vao check ket qua " + jResultObject.getInt("code"));
				/* Check if result not found */
				if (jResultObject.getInt("code") != 0) {
					return null;
				} else {
					JSONArray jArray = json.getJSONArray("ranking");
					for (int i = 0; i < jArray.length(); i++) {
						JSONObject jObject = jArray.getJSONObject(i);
						LocalRankingInfo localRankingInfo = new LocalRankingInfo();
						localRankingInfo.setPosition(i + 1);
						localRankingInfo.setNurseType(jObject.getInt("ntype"));
						localRankingInfo.setTiredScore(jObject.getInt("tired"));
						localRankingInfo.setGlobalPosition(jObject
								.getString("rank"));
						Log.d("rank", "" + jObject.getString("rank"));
						listLocalServerRank.add(localRankingInfo);
					}
					Collections.sort(listLocalServerRank, new RankComparator());
					return listLocalServerRank;
				}
			} catch (JSONException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
				return null;
			}
		}
	}

	private boolean updateGlobalRankForLocalFile(String url, boolean showFlag,
			int protocol) {
		listServerRank = this.getServerRank(url);
		if (listServerRank != null && showFlag) {
			if (protocol == GET) {
				for (int i = 0; i < listLocalRank.size(); i++) {
					for (int j = 0; j < listServerRank.size(); j++) {
						if (listLocalRank.get(i).getNurseType() == listServerRank
								.get(j).getNurseType()
								&& listLocalRank.get(i).getTiredScore() == listServerRank
										.get(j).getTiredScore()) {
							listLocalRank.get(i).setGlobalPosition(
									listServerRank.get(j).getGlobalPosition());
						}
					}
					if(listLocalRank.get(i).getGlobalPosition().equals("null")){
						listLocalRank.get(i).setGlobalPosition("--/--");
					}
				}
				this.updateLocalRankingFile();
			}
			return true;
		} else {
			for (int i = 0; i < listLocalRank.size(); i++) {
				listLocalRank.get(i).setGlobalPosition("null");
			}
			this.updateLocalRankingFile();
			
			if (listServerRank == null) {

				return false;
			}
			return true;
		}
	}

	private ArrayList<LocalRankingInfo> getWaitListFromLocal() {

		String waitString = readFile("/wait.json");
		if (waitString == null) {
			Log.d("waitString", "Null oy");
			return null;
		} else {
			Log.d("waitString", waitString);
			ArrayList<LocalRankingInfo> waitList = new ArrayList<LocalRankingInfo>();
			try {
				JSONObject json = new JSONObject(waitString);
				JSONArray jArray = json.getJSONArray("post_wait_result");
				for (int i = 0; i < jArray.length(); i++) {
					LocalRankingInfo localRankingInfo = new LocalRankingInfo();
					JSONObject j = jArray.getJSONObject(i);
					localRankingInfo.setUsername(j.getString("name"));
					localRankingInfo.setAge(j.getInt("age"));
					localRankingInfo.setNurseType(j.getInt("type"));
					localRankingInfo.setTiredScore(j.getInt("tired"));
					waitList.add(localRankingInfo);
				}
				return waitList;
			} catch (JSONException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
				return null;
			}
		}
	}

	private ArrayList<LocalRankingInfo> addWaitList(
			LocalRankingInfo localRankingInfo) {
		ArrayList<LocalRankingInfo> list = getWaitListFromLocal();
		if (list == null) {
			list = new ArrayList<LocalRankingInfo>();
		}
		if(list.size() == 0){
			list.add(localRankingInfo);
		}
		
		return list;
	}

	private ArrayList<LocalRankingInfo> removeWaitList(
			LocalRankingInfo localRankingInfo) {
		ArrayList<LocalRankingInfo> list = getWaitListFromLocal();
		for (int i = 0; i < list.size(); i++) {
			if (list.get(i).getAge() == localRankingInfo.getAge()
					&& list.get(i).getNurseType() == localRankingInfo
							.getNurseType()
					&& list.get(i).getTiredScore() == localRankingInfo
							.getTiredScore()
					&& list.get(i).getUsername()
							.equals(localRankingInfo.getUsername())) {
				list.remove(i);
				break;
			}
		}

		return list;
	}

	private boolean writeToWaitListFile(LocalRankingInfo ls, int flag) {
		ArrayList<LocalRankingInfo> list = null;
		if (ls != null) {
			if (flag == ADD) {
				list = this.addWaitList(ls);
			} else {
				list = this.removeWaitList(ls);
			}
			JSONArray jArray = new JSONArray();
			for (int i = 0; i < list.size(); i++) {
				LocalRankingInfo l = new LocalRankingInfo();
				l = list.get(i);
				JSONObject j = new JSONObject();
				try {
					j.put("type", l.getNurseType());
					j.put("tired", l.getTiredScore());
					j.put("name", l.getUsername());
					j.put("age", l.getAge());
					jArray.put(j);
				} catch (JSONException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
					return false;
				}
			}
			String stringToWrite = jArray.toString();
			stringToWrite = "{\"post_wait_result\": " + stringToWrite + " }";
			Log.d("WriteWait", stringToWrite);
			writeFile(stringToWrite, "wait.json");
			return true;
		} else {
			return false;
		}
	}

	public void tryToResend(String url, boolean showFlag) {
		ArrayList<LocalRankingInfo> list = this.getWaitListFromLocal();
		Log.d("try", "Da vao try");
		if (list != null && list.size() > 0) {
			for (int i = 0; i < list.size(); i++) {
				this.newRank = list.get(i);
				String urls = this.makeUrl(url, 1);
				if (this.updateGlobalRankForLocalFile(urls, showFlag, POST)) {
					this.writeToWaitListFile(newRank, REMOVE);
					Log.d("post","Post sucess :"+i);
				}else {
					Log.d("post","Post faile");
					break;
				}
			}
		} else {
			Log.d("ls", "List null");
		}
	}

	public ArrayList<LocalRankingInfo> getListLocalRank() {
		return listLocalRank;
	}

}
